//
//  RWDiskStore.m
//  XYYCache_Example
//
//  Created by ioszhb on 2020/8/13.
//  Copyright © 2020 developzhb. All rights reserved.
//

#import "RWDiskStore.h"
#import <CommonCrypto/CommonCrypto.h>
#import <objc/runtime.h>

/// String's md5 hash.
static NSString *XY_NSStringMD5(NSString *string) {
    if (!string) return nil;
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5(data.bytes, (CC_LONG)data.length, result);
    return [NSString stringWithFormat:
                @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                result[0],  result[1],  result[2],  result[3],
                result[4],  result[5],  result[6],  result[7],
                result[8],  result[9],  result[10], result[11],
                result[12], result[13], result[14], result[15]
            ];
}

@interface _XYStoreItem : NSObject
@property (nonatomic, copy) NSString *key;
@property (nonatomic, copy) NSString *filename;
@property (nonatomic, copy) Class fileclass;
@property (nonatomic, copy) NSString *filepath;
/// 记录根路径,用于类删除
@property (nonatomic, copy) NSString *rootpath;
@end

@implementation _XYStoreItem

@end



@interface RWDiskStore ()

@end

@implementation RWDiskStore

#pragma mark - *************************************************************************
#pragma mark construct

- (instancetype) init {
    NSLog(@"Use \"initWithName\" or \"initWithPath\" to create RWCache instance.");
    return [self initWithPath:@""];
}

- (instancetype)initWithName:(NSString *)name {
    if (name.length == 0) return nil;
    NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *path = [documentPath stringByAppendingPathComponent:name];
    NSLog(@"🔥%@🔥", path);
    return [self initWithPath:path];
}

- (instancetype)initWithPath:(NSString *)path {
    if (path.length == 0) return nil;
    self = [super init];
    if (self) {
        _path = path;
        _name = [path lastPathComponent];
    }
    return self;
}

+ (instancetype)diskStoreWithName:(NSString *)name {
    return [[self alloc] initWithName:name];
}

+ (instancetype)diskStoreWithPath:(NSString *)path {
    return [[self alloc] initWithPath:path];
}

+ (instancetype)defaultManager {
    static RWDiskStore *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] initWithName:@"COM.XYDiskStore"];
    });
    return instance;
}

#pragma mark - *************************************************************************
#pragma mark tool.function

- (BOOL)containsObjectForKey:(NSString *)key {
    NSString *filepath = [self p_filepathWithKey:key];
    return [NSFileManager.defaultManager fileExistsAtPath:filepath];
}

- (void)setObject:(nullable id<NSSecureCoding>)object forKey:(NSString *)key {
    if (key==nil || key.length == 0) return;
    if (object==nil) {
        [self removeObjectForKey:key];
        return;
    }
    NSData *result = nil;
    @try {
        if (@available(iOS 11.0, *)) {
            NSError *error;
            result = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:YES error:&error];
            if (error) {
                NSLog(@"🍵_-setObject:forKey:,建议<NSSecureCoding>,&&-(BOOL)supportsSecureCoding方法返回YES. error:%@__", error);
            }
        }else {
            NSMutableData *tempData = [NSMutableData data];
            NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:tempData];
            [archiver setRequiresSecureCoding:YES];
            [archiver encodeObject:object forKey:NSKeyedArchiveRootObjectKey];
            [archiver finishEncoding];
            result = tempData;
        }

//        if (@available(iOS 11.0, *)) {
//            NSError *error;
//            value = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:YES error:&error];
//            if (error) {
//                NSLog(@"🍵_-setObject:forKey:,建议<NSSecureCoding>,&&-(BOOL)supportsSecureCoding方法返回YES. error:%@__", error);
//            }
//        }else {
//            value = [NSKeyedArchiver archivedDataWithRootObject:object];
//        }
    }
    @catch (NSException *exception) {
        // nothing to do...
        [self removeObjectForKey:key];
    }
    if (!result) return;
    [self p_writeData:result forKey:key dataClass:object_getClass(object)];
}

- (void)updateObject:(nullable id<NSSecureCoding>)object forKey:(NSString *)key {
    [self setObject:object forKey:key];
}

- (nullable id<NSSecureCoding>)objectForKey:(NSString *)key forClass:(Class)classType {
    if (key==nil || key.length==0) return nil;

    //验证path
    NSString *filepath = [self p_filepathWithKey:key];
    //读取
    id result = nil;
    if ([[NSFileManager defaultManager] fileExistsAtPath:filepath]) {
        
        @try {
            if (@available(iOS 11.0, *)) {
                NSError *error;
                NSData *fromData = [NSData dataWithContentsOfFile:filepath];
                NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:fromData error:&error];
                [unarchiver setRequiresSecureCoding:YES];
                result = [unarchiver decodeObjectOfClass:[classType class] forKey:NSKeyedArchiveRootObjectKey];
            }else {
                NSData *fromData = [NSData dataWithContentsOfFile:filepath];
                NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:fromData];
                [unarchiver setRequiresSecureCoding:YES];
                result = [unarchiver decodeObjectOfClass:[classType class] forKey:NSKeyedArchiveRootObjectKey];
            }
            
//            if (@available(iOS 11.0, *)) {
//                NSError *error;
//                NSData *fromData = [NSData dataWithContentsOfFile:filepath];
//                data = [NSKeyedUnarchiver unarchivedObjectOfClass:[classType class] fromData:fromData error:&error];
//                if (error) {
//                    NSLog(@"🍵_-objectForKey:, 建议<NSSecureCoding> error:%@__", error);
//                }
//            }else {
//                data = [NSKeyedUnarchiver unarchiveObjectWithFile:filepath];
//            }
        }
        @catch (NSException *exception) {
            [self removeObjectForKey:key];
        }
    }
    return result;
}


- (void)removeObjectForKey:(NSString *)key {
    if (key==nil || key.length==0) return;
    NSString *filepath = [self p_filepathWithKey:key];
    if (![NSFileManager.defaultManager fileExistsAtPath:filepath]) {
        return ;
    }
    NSError *error;
    if ([NSFileManager.defaultManager removeItemAtPath:filepath error:&error]) {
        
    }else {
        NSLog(@"🍵_-removeObjectForKey, error:%@__", error);
    }
}

- (void)removeAllObjects {
    NSError *error;
    if (![NSFileManager.defaultManager fileExistsAtPath:_path]) {
        return ;
    }
    if ([NSFileManager.defaultManager removeItemAtPath:_path error:&error]) {
        
    }else {
        NSLog(@"🍵_-removeAllObjects, error:%@__", error);
    }
}

+ (void)removeObjectsAtPath:(NSString *)path {
    if (path==nil || path.length==0) return;
    NSError *error;
    if (![NSFileManager.defaultManager fileExistsAtPath:path]) {
        return ;
    }
    if ([NSFileManager.defaultManager removeItemAtPath:path error:&error]) {
        
    }else {
        NSLog(@"🍵_-removeAllObjects, error:%@__", error);
    }
}

+ (void)removeObjectsWithName:(NSString *)name {
    if (name==nil || name.length==0) return;
    NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *path = [documentPath stringByAppendingPathComponent:name];
    NSError *error;
    if (![NSFileManager.defaultManager fileExistsAtPath:path]) {
        return ;
    }
    if ([NSFileManager.defaultManager removeItemAtPath:path error:&error]) {
        
    }else {
        NSLog(@"🍵_-removeAllObjects, error:%@__", error);
    }
}

/// 根据key获取文件路径(warning: 只有key值对应的文件保存成功且在内存中,才有返回值.否则return nil)
/// @param key 查找文件的键
- (NSString *)readFilePathForKey:(NSString *)key {
    if (key==nil || key.length==0) return nil;
    NSString *filepath = [self p_filepathWithKey:key];
    if ([NSFileManager.defaultManager fileExistsAtPath:filepath]) {
        return filepath;
    }
    return nil;
}

/// 根据key获取文件的二进制数据(warning: 只有key值对应的文件保存成功且在内存中,才有返回值.否则return nil)
/// @param key 查找文件的键
- (NSData *)readDataForKey:(NSString *)key {
    if (key==nil || key.length==0) return nil;
    NSString *filepath = [self p_filepathWithKey:key];
    if ([NSFileManager.defaultManager fileExistsAtPath:filepath]) {
        return [NSData dataWithContentsOfFile:filepath];
    }
    return nil;
}


#pragma mark - *************************************************************************
#pragma mark Getter

#pragma mark - *************************************************************************
#pragma mark Private
- (NSString *)p_filenameWithKey:(NSString *)key {
    if (key==nil || key.length==0) { return nil; }
    return XY_NSStringMD5(key);
}

- (NSString *)p_filepathWithKey:(NSString *)key {
    if (key==nil || key.length==0) { return nil; }
    return [_path stringByAppendingPathComponent:[self p_filenameWithKey:key]];
}

+ (BOOL)p_buildDirectoryIfNeedWithPath:(NSString *)path {
    BOOL isDir;
    if ([NSFileManager.defaultManager fileExistsAtPath:path isDirectory:&isDir]) {
        if (isDir) {//存在文件,且是文件夹
            return YES;
        }
    }
    NSError *error;
    BOOL buildSuccess = [NSFileManager.defaultManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
    if (buildSuccess) {
        return YES;
    }else {
        NSLog(@"🍵_-p_buildDirectoryIfNeedWithPath, error:%@__", error);
        return NO;
    }
}

- (BOOL)p_writeData:(NSData *)data forKey:(NSString *)key dataClass:(Class)cls {
    if (key==nil || key.length==0) { return NO; }

    BOOL isExist = [RWDiskStore p_buildDirectoryIfNeedWithPath:_path];
    if (isExist==NO) { return NO; }

    NSString *filepath = [self p_filepathWithKey:key];
    BOOL isSuccess = [data writeToFile:filepath atomically:YES];
    return isSuccess;
}

@end






















































//#import <time.h>
//
//#if __has_include(<sqlite3.h>)
//#import <sqlite3.h>
//#else
//#import "sqlite3.h"
//#endif
//
//
//static const NSUInteger kMaxErrorRetryCount = 8;
//static const NSTimeInterval kMinRetryTimeInterval = 2.0;
//static const int kPathLengthMax = PATH_MAX - 64;
//static NSString *const kDBFileName = @"manifest.sqlite";
//static NSString *const kDBShmFileName = @"manifest.sqlite-shm";
//static NSString *const kDBWalFileName = @"manifest.sqlite-wal";
//static NSString *const kDataDirectoryName = @"data";
//static NSString *const kTrashDirectoryName = @"trash";
//
///*
// File:
// /path/
//      /disktable.sqlite
//      /disktable.sqlite-shm
//      /disktable.sqlite-wal
//      /trash/
//            /unused_file_or_folder
//
// SQL:
// create table if not exists disktable (
//    key                 text,
//    path                text,
//    filename            text,
//    filepath            text,
//    fileclass           text,
//    size                integer,
//    modification_time   integer,
//    last_access_time    integer,
//    inline_data         blob,
//    extended_data       blob,
//    primary key(filepath)
// );
// create index if not exists last_access_time_idx on disktable(last_access_time);
// */
//
//
///// String's md5 hash.
//static NSString *_XYYStringMD5(NSString *string) {
//    if (!string) return nil;
//    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
//    unsigned char result[CC_MD5_DIGEST_LENGTH];
//    CC_MD5(data.bytes, (CC_LONG)data.length, result);
//    return [NSString stringWithFormat:
//                @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
//                result[0],  result[1],  result[2],  result[3],
//                result[4],  result[5],  result[6],  result[7],
//                result[8],  result[9],  result[10], result[11],
//                result[12], result[13], result[14], result[15]
//            ];
//}
//
//static NSString *_XYStringFilter(NSString *string) {
//    if (string==nil || string.length==0) return nil;
//
//    string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
//    NSArray *components = [string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
//    components = [components filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != '\\s'"]];
//    string = [components componentsJoinedByString:@""];
//    return string;
//}
//
//static UIApplication *_XYYSharedApplication() {
//    static BOOL isAppExtension = NO;
//    static dispatch_once_t onceToken;
//    dispatch_once(&onceToken, ^{
//        Class cls = NSClassFromString(@"UIApplication");
//        if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES;
//        if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES;
//    });
//#pragma clang diagnostic push
//#pragma clang diagnostic ignored "-Wundeclared-selector"
//    return isAppExtension ? nil : [UIApplication performSelector:@selector(sharedApplication)];
//#pragma clang diagnostic pop
//}
//
//@interface _XYStoreItem : NSObject
//@property (nonatomic, copy) NSString *key;          ///< 外界传入的key
//@property (nonatomic, strong) NSData *value;        ///< 外界传入数据的二级制流
//@property (nonatomic, copy) NSString *filename;     ///< 内部根据key生成的'文件名',这里是key的md5的值
//@property (nonatomic, copy) NSString *filepath;     ///< filepath是_path/filename,绝对路径
//@property (nonatomic, copy) NSString *fileclass;    ///< 保存的数据的类型
//@property (nonatomic) int size;                     ///< value's size in bytes
//@property (nonatomic) int modTime;                  ///< 最后一次修改的时间戳
//@property (nonatomic) int accessTime;               ///< 最后一次访问的时间戳
//@property (nonatomic, strong) NSData *extendedData; ///< extended data (nil if no extended data)
//@property (nonatomic, copy) NSString *path;         ///< XYDiskStore的_path,用于批量删除
//@property (nonatomic, copy) NSString *version;      ///< XYDiskStore的_path,用于批量删除
//@end
//
//@implementation _XYStoreItem
//@end
//
//
//@interface RWDiskStore ()
//{
//    dispatch_queue_t _trashQueue;
//
//    NSString *_path;
//    NSString *_dbPath;
//    NSString *_dataPath;
//    NSString *_trashPath;
//
//    sqlite3 *_db;
//    CFMutableDictionaryRef _dbStmtCache;//db操作语句 缓存
//    NSTimeInterval _dbLastOpenErrorTime;
//    NSUInteger _dbOpenErrorCount;
//}
//@end
//
//
//@implementation RWDiskStore
//
//- (NSString *)_filenameForKey:(NSString *)key {
//    if (!key || key.length==0) return nil;
//    return (_XYYStringMD5(key));
//}
//
//- (NSString *)_filepathForKey:(NSString *)key {
//    if (!key || key.length==0) return nil;
//    return [_path stringByAppendingPathComponent:[self _filenameForKey:key]];
//}
//
//#pragma mark - db
//
//- (BOOL)_dbOpen {
//    if (_db) return YES;
//
//    int result = sqlite3_open(_dbPath.UTF8String, &_db);
//    if (result == SQLITE_OK) {
//        CFDictionaryKeyCallBacks keyCallbacks = kCFCopyStringDictionaryKeyCallBacks;
//        CFDictionaryValueCallBacks valueCallbacks = {0};
//        //初始化数据操作句柄缓存池
//        _dbStmtCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &keyCallbacks, &valueCallbacks);
//        _dbLastOpenErrorTime = 0;
//        _dbOpenErrorCount = 0;
//        return YES;
//    } else {
//        _db = NULL;
//        if (_dbStmtCache) CFRelease(_dbStmtCache);
//        _dbStmtCache = NULL;
//        _dbLastOpenErrorTime = CACurrentMediaTime();
//        _dbOpenErrorCount++;
//
//        if (_errorLogsEnabled) {
//            NSLog(@"%s line:%d sqlite open failed (%d).", __FUNCTION__, __LINE__, result);
//        }
//        return NO;
//    }
//}
//
//- (BOOL)_dbClose {
//    if (!_db) return YES;
//
//    int  result = 0;
//    BOOL retry = NO;
//    BOOL stmtFinalized = NO;
//
//    //释放数据操作句柄缓存池
//    if (_dbStmtCache) CFRelease(_dbStmtCache);
//    _dbStmtCache = NULL;
//
//    do {
//        retry = NO;
//        result = sqlite3_close(_db);
//        if (result == SQLITE_BUSY || result == SQLITE_LOCKED) {
//            if (!stmtFinalized) {
//                stmtFinalized = YES;
//                sqlite3_stmt *stmt;
//                while ((stmt = sqlite3_next_stmt(_db, nil)) != 0) {
//                    sqlite3_finalize(stmt);
//                    retry = YES;
//                }
//            }
//        } else if (result != SQLITE_OK) {
//            if (_errorLogsEnabled) {
//                NSLog(@"%s line:%d sqlite close failed (%d).", __FUNCTION__, __LINE__, result);
//            }
//        }
//    } while (retry);
//    _db = NULL;
//    return YES;
//}
//
//- (BOOL)_dbCheck {
//    if (!_db) {
//        if (_dbOpenErrorCount < kMaxErrorRetryCount &&
//            CACurrentMediaTime() - _dbLastOpenErrorTime > kMinRetryTimeInterval) {
//            return [self _dbOpen] && [self _dbInitialize];
//        } else {
//            return NO;
//        }
//    }
//    return YES;
//}
//
//- (BOOL)_dbInitialize {
//    NSString *sql = @"pragma journal_mode = wal; pragma synchronous = normal; create table if not exists disktable (key text, path text, filename text, filepath text, fileclass text, size integer, modification_time integer, last_access_time integer, inline_data blob, extended_data blob, primary key(filepath)); create index if not exists last_access_time_idx on disktable(last_access_time);";
//    return [self _dbExecute:sql];
//}
//
//- (void)_dbCheckpoint {
//    if (![self _dbCheck]) return;
//    // Cause a checkpoint to occur, merge `sqlite-wal` file to `sqlite` file.
//    sqlite3_wal_checkpoint(_db, NULL);
//}
//
//- (BOOL)_dbExecute:(NSString *)sql {
//    if (sql.length == 0) return NO;
//    if (![self _dbCheck]) return NO;
//
//    char *error = NULL;
//    int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &error);
//    if (error) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite exec error (%d): %s", __FUNCTION__, __LINE__, result, error);
//        sqlite3_free(error);
//    }
//
//    return result == SQLITE_OK;
//}
//
///// 读取sql句柄,没有缓存中没有,则保存到缓存池中
//- (sqlite3_stmt *)_dbPrepareStmt:(NSString *)sql {
//    if (![self _dbCheck] || sql.length == 0 || !_dbStmtCache) return NULL;
//    sqlite3_stmt *stmt = (sqlite3_stmt *)CFDictionaryGetValue(_dbStmtCache, (__bridge const void *)(sql));
//    if (!stmt) {
//        int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
//        if (result != SQLITE_OK) {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//            return NULL;
//        }
//        CFDictionarySetValue(_dbStmtCache, (__bridge const void *)(sql), stmt);
//    } else {
//        sqlite3_reset(stmt);
//    }
//    return stmt;
//}
//
//- (NSString *)_dbJoinedKeys:(NSArray *)keys {
//    NSMutableString *string = [NSMutableString new];
//    for (NSUInteger i = 0,max = keys.count; i < max; i++) {
//        [string appendString:@"?"];
//        if (i + 1 != max) {
//            [string appendString:@","];
//        }
//    }
//    return string;
//}
//
//
//- (void)_dbBindJoinedKeys:(NSArray *)keys stmt:(sqlite3_stmt *)stmt fromIndex:(int)index{
//    for (int i = 0, max = (int)keys.count; i < max; i++) {
//        NSString *filepath = [self _filepathForKey:keys[i]];
//        sqlite3_bind_text(stmt, index + i, filepath.UTF8String, -1, NULL);
//    }
////    for (int i = 0, max = (int)keys.count; i < max; i++) {
////        NSString *key = keys[i];
////        sqlite3_bind_text(stmt, index + i, key.UTF8String, -1, NULL);
////    }
//}
//
//- (BOOL)_dbSaveWithKey:(NSString *)key filename:(NSString *)filename fileclass:(NSString *)classstr sizebytes:(int)sizebytes data:(NSData *)data extendedData:(NSData *)extendedData {
//    NSString *sql = @"insert or replace into disktable (key, path, filename, filepath, fileclass, size, modification_time, last_access_time, inline_data, extended_data) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10);";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return NO;
//
//    NSString *filepath = [_path stringByAppendingPathComponent:filename];
//
//    int timestamp = (int)time(NULL);
//    sqlite3_bind_text(stmt, 1, key.UTF8String, -1, NULL);                           //< 1.key
//    sqlite3_bind_text(stmt, 2, _path.UTF8String, -1, NULL);                         //< 2.path
//    sqlite3_bind_text(stmt, 3, filename.UTF8String, -1, NULL);                      //< 3.filename
//    sqlite3_bind_text(stmt, 4, filepath.UTF8String, -1, NULL);                      //< 4.filepath
//    sqlite3_bind_text(stmt, 5, classstr.UTF8String, -1, NULL);                      //< 5.fileclass
//    sqlite3_bind_int(stmt, 6, (int)sizebytes);                                      //< 6.size
//    sqlite3_bind_int(stmt, 7, timestamp);                                           //< 7.modification_time
//    sqlite3_bind_int(stmt, 8, timestamp);                                           //< 8.last_access_time
////    sqlite3_bind_blob(stmt, 9, data.bytes, (int)data.length, 0);                    //< 9.inline_data..今后扩展用.
//    sqlite3_bind_blob(stmt, 9, NULL, 0, 0);
//    sqlite3_bind_blob(stmt, 10, extendedData.bytes, (int)extendedData.length, 0);   //< 10.extended_data
//
//    int result = sqlite3_step(stmt);
//    if (result != SQLITE_DONE) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite insert error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//    return YES;
//}
//
////- (BOOL)_dbSaveWithKey:(NSString *)key value:(NSData *)value fileName:(NSString *)fileName extendedData:(NSData *)extendedData {
////
////    NSString *sql = @"insert or replace into disktable (key, filename, filepath, fileclass, size, modification_time, last_access_time, extended_data, path) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9);";
////    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
////    if (!stmt) return NO;
////
////    int timestamp = (int)time(NULL);
////    sqlite3_bind_text(stmt, 1, key.UTF8String, -1, NULL);           //< 1.key
////    sqlite3_bind_text(stmt, 2, fileName.UTF8String, -1, NULL);      //< 2.filename
////    sqlite3_bind_int(stmt, 3, (int)value.length);
////    if (fileName.length == 0) {
////        sqlite3_bind_blob(stmt, 4, value.bytes, (int)value.length, 0);
////    } else {
////        sqlite3_bind_blob(stmt, 4, NULL, 0, 0);
////    }
////    sqlite3_bind_int(stmt, 5, timestamp);
////    sqlite3_bind_int(stmt, 6, timestamp);
////    sqlite3_bind_blob(stmt, 7, extendedData.bytes, (int)extendedData.length, 0);
////
////    int result = sqlite3_step(stmt);
////    if (result != SQLITE_DONE) {
////        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite insert error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
////        return NO;
////    }
////    return YES;
////}
//
//- (BOOL)_dbUpdateAccessTimeWithKey:(NSString *)key {
//    if (key==nil || key.length==0) return NO;
//    if (![self _dbCheck]) return NO;
//
//    NSString *sql = @"update disktable set last_access_time = ?1 where filepath = ?2;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return NO;
//
//    int timestamp = (int)time(NULL);
//    NSString *filepath = [self _filepathForKey:key];
//    sqlite3_bind_int(stmt, 1, timestamp);
//    sqlite3_bind_text(stmt, 2, filepath.UTF8String, -1, NULL);
//
//    int result = sqlite3_step(stmt);
//    if (result != SQLITE_DONE) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite update error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//    return YES;
//}
//
//- (BOOL)_dbUpdateAccessTimeWithKeys:(NSArray *)keys {
//    if (keys==nil || keys.count==0) return NO;
//    if (![self _dbCheck]) return NO;
//
//    int t = (int)time(NULL);
//    NSString *sql = [NSString stringWithFormat:@"update disktable set last_access_time = %d where filepath in (%@);", t, [self _dbJoinedKeys:keys]];
//    sqlite3_stmt *stmt = NULL;
//    int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
//    if (result != SQLITE_OK) {
//        if (_errorLogsEnabled)  NSLog(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//
//    [self _dbBindJoinedKeys:keys stmt:stmt fromIndex:1];
//    result = sqlite3_step(stmt);
//    sqlite3_finalize(stmt);
//    if (result != SQLITE_DONE) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite update error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//    return YES;
//}
//
//- (BOOL)_dbDeleteItemWithKey:(NSString *)key {
//    if (key==nil || key.length==0) return NO;
//
//    NSString *filepath = [self _filepathForKey:key];
//    NSString *sql = @"delete from disktable where filepath = ?1;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return NO;
//
//    sqlite3_bind_text(stmt, 1, filepath.UTF8String, -1, NULL);
//
//    int result = sqlite3_step(stmt);
//    if (result != SQLITE_DONE) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d db delete error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//    return YES;
//}
//
//- (BOOL)_dbDeleteItemWithPath:(NSString *)filepath {
//    if (filepath==nil || filepath.length==0) return NO;
//    NSString *sql = @"delete from disktable where filepath = ?1;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return NO;
//    sqlite3_bind_text(stmt, 1, filepath.UTF8String, -1, NULL);
//    int result = sqlite3_step(stmt);
//    if (result != SQLITE_DONE) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d db delete error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//    return YES;
//}
//
//
//
//- (BOOL)_dbDeleteItemWithKeys:(NSArray *)keys {
//    if (keys==nil || keys.count==0) return NO;
//    if (![self _dbCheck]) return NO;
//
//    NSString *sql =  [NSString stringWithFormat:@"delete from disktable where filepath in (%@);", [self _dbJoinedKeys:keys]];
//    sqlite3_stmt *stmt = NULL;
//    int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
//    if (result != SQLITE_OK) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//
//    [self _dbBindJoinedKeys:keys stmt:stmt fromIndex:1];
//    result = sqlite3_step(stmt);
//    sqlite3_finalize(stmt);
//    if (result == SQLITE_ERROR) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite delete error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//    return YES;
//}
//
//- (BOOL)_dbDeleteItemsWithSizeLargerThan:(int)size {
//    NSString *sql = @"delete from disktable where size > ?1;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return NO;
//    sqlite3_bind_int(stmt, 1, size);
//    int result = sqlite3_step(stmt);
//    if (result != SQLITE_DONE) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite delete error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//    return YES;
//}
//
//- (BOOL)_dbDeleteItemsWithTimeEarlierThan:(int)time {
//    NSString *sql = @"delete from disktable where last_access_time < ?1;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return NO;
//    sqlite3_bind_int(stmt, 1, time);
//    int result = sqlite3_step(stmt);
//    if (result != SQLITE_DONE) {
//        if (_errorLogsEnabled)  NSLog(@"%s line:%d sqlite delete error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return NO;
//    }
//    return YES;
//}
//
//- (_XYStoreItem *)_dbGetItemFromStmt:(sqlite3_stmt *)stmt excludeInlineData:(BOOL)excludeInlineData {
//    int i = 0;
//    char *key = (char *)sqlite3_column_text(stmt, i++);
//    char *path = (char *)sqlite3_column_text(stmt, i++);
//    char *filename = (char *)sqlite3_column_text(stmt, i++);
//    char *filepath = (char *)sqlite3_column_text(stmt, i++);
//    char *fileclass = (char *)sqlite3_column_text(stmt, i++);
//    int size = sqlite3_column_int(stmt, i++);
//    int modification_time = sqlite3_column_int(stmt, i++);
//    int last_access_time = sqlite3_column_int(stmt, i++);
//
//    const void *inline_data = excludeInlineData ? NULL : sqlite3_column_blob(stmt, i);
//    int inline_data_bytes = excludeInlineData ? 0 : sqlite3_column_bytes(stmt, i++);
//
//    const void *extended_data = sqlite3_column_blob(stmt, i);
//    int extended_data_bytes = sqlite3_column_bytes(stmt, i++);
//
//    _XYStoreItem *item = [_XYStoreItem new];
//    if (key) item.key = [NSString stringWithUTF8String:key];
//    if (path) item.path = [NSString stringWithUTF8String:path];
//    if (filename) item.filename = [NSString stringWithUTF8String:filename];
//    if (filepath) item.filename = [NSString stringWithUTF8String:filepath];
//    if (fileclass) item.fileclass = [NSString stringWithUTF8String:fileclass];
//    item.size = size;
//    item.modTime = modification_time;
//    item.accessTime = last_access_time;
//
//    if (inline_data_bytes > 0 && inline_data) item.value = [NSData dataWithBytes:inline_data length:inline_data_bytes];
//    if (extended_data_bytes > 0 && extended_data) item.extendedData = [NSData dataWithBytes:extended_data length:extended_data_bytes];
//
//    return item;
//}
//
//- (_XYStoreItem *)_dbGetItemWithKey:(NSString *)key excludeInlineData:(BOOL)excludeInlineData {
//    if (key==nil || key.length==0) return nil;
//    NSString *sql = nil;
//    if (excludeInlineData) {
//        sql = @"select key, path, filename, filepath, fileclass, size, modification_time, last_access_time, extended_data from disktable where filepath = ?1;";
//    }else {
//        sql = @"select key, path, filename, filepath, fileclass, size, modification_time, last_access_time, inline_data, extended_data from disktable where filepath = ?1;";
//    }
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return nil;
//
//    NSString *filepath = [self _filepathForKey:key];
//    sqlite3_bind_text(stmt, 1, filepath.UTF8String, -1, NULL);
//
//    _XYStoreItem *item = nil;
//    int result = sqlite3_step(stmt);
//    if (result == SQLITE_ROW) {
//        item = [self _dbGetItemFromStmt:stmt excludeInlineData:excludeInlineData];
//    } else {
//        if (result != SQLITE_DONE) {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        }
//    }
//    return item;
//}
//
//- (NSMutableArray *)_dbGetItemWithKeys:(NSArray *)keys excludeInlineData:(BOOL)excludeInlineData {
//    if (keys==nil || keys.count==0) return nil;
//    if (![self _dbCheck]) return nil;
//    NSString *sql;
//    if (excludeInlineData) {
//        sql = [NSString stringWithFormat:@"select key, path, filename, filepath, fileclass, size, modification_time, last_access_time, extended_data from disktable where filepath in (%@);", [self _dbJoinedKeys:keys]];
//    } else {
//        sql = [NSString stringWithFormat:@"select key, path, filename, filepath, fileclass, size, modification_time, last_access_time, inline_data, extended_data from disktable where filepath in (%@)", [self _dbJoinedKeys:keys]];
//    }
//
//    sqlite3_stmt *stmt = NULL;
//    int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
//    if (result != SQLITE_OK) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return nil;
//    }
//
//    [self _dbBindJoinedKeys:keys stmt:stmt fromIndex:1];
//    NSMutableArray *items = [NSMutableArray new];
//    do {
//        result = sqlite3_step(stmt);
//        if (result == SQLITE_ROW) {
//            _XYStoreItem *item = [self _dbGetItemFromStmt:stmt excludeInlineData:excludeInlineData];
//            if (item) [items addObject:item];
//        } else if (result == SQLITE_DONE) {
//            break;
//        } else {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//            items = nil;
//            break;
//        }
//    } while (1);
//    sqlite3_finalize(stmt);
//    return items;
//}
//
//- (NSData *)_dbGetValueWithKey:(NSString *)key {
//    if (key==nil || key.length==0) return nil;
//    NSString *sql = @"select inline_data from disktable where filepath = ?1;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return nil;
//
//    NSString *filepath = [self _filepathForKey:key];
//    sqlite3_bind_text(stmt, 1, filepath.UTF8String, -1, NULL);
//
//    int result = sqlite3_step(stmt);
//    if (result == SQLITE_ROW) {
//        const void *inline_data = sqlite3_column_blob(stmt, 0);
//        int inline_data_bytes = sqlite3_column_bytes(stmt, 0);
//        if (!inline_data || inline_data_bytes <= 0) return nil;
//        return [NSData dataWithBytes:inline_data length:inline_data_bytes];
//    } else {
//        if (result != SQLITE_DONE) {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        }
//        return nil;
//    }
//}
//
//- (NSString *)_dbGetFilenameWithKey:(NSString *)key {
//    if (key==nil || key.length==0) return nil;
//    NSString *sql = @"select filename from disktable where filepath = ?1;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return nil;
//
//    NSString *filepath = [self _filepathForKey:key];
//    sqlite3_bind_text(stmt, 1, filepath.UTF8String, -1, NULL);
//    int result = sqlite3_step(stmt);
//    if (result == SQLITE_ROW) {
//        char *filename = (char *)sqlite3_column_text(stmt, 0);
//        if (filename && *filename != 0) {
//            return [NSString stringWithUTF8String:filename];
//        }
//    } else {
//        if (result != SQLITE_DONE) {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        }
//    }
//    return nil;
//}
//
//
//- (NSString *)_dbGetFilepathWithKey:(NSString *)key {
//    if (key==nil || key.length==0) return nil;
//    NSString *sql = @"select filepath from disktable where filepath = ?1;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return nil;
//
//    NSString *filepath = [self _filepathForKey:key];
//    sqlite3_bind_text(stmt, 1, filepath.UTF8String, -1, NULL);
//    int result = sqlite3_step(stmt);
//    if (result == SQLITE_ROW) {
//        char *filename = (char *)sqlite3_column_text(stmt, 0);
//        if (filename && *filename != 0) {
//            return [NSString stringWithUTF8String:filename];
//        }
//    } else {
//        if (result != SQLITE_DONE) {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        }
//    }
//    return nil;
//}
//
//- (NSMutableArray *)_dbGetFilenameWithKeys:(NSArray *)keys {
//    if (keys==nil || keys.count==0) return nil;
//    if (![self _dbCheck]) return nil;
//
//    NSString *sql = [NSString stringWithFormat:@"select filename from disktable where filepath in (%@);", [self _dbJoinedKeys:keys]];
//    sqlite3_stmt *stmt = NULL;
//    int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
//    if (result != SQLITE_OK) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return nil;
//    }
//
//    [self _dbBindJoinedKeys:keys stmt:stmt fromIndex:1];
//    NSMutableArray *filenames = [NSMutableArray new];
//    do {
//        result = sqlite3_step(stmt);
//        if (result == SQLITE_ROW) {
//            char *filename = (char *)sqlite3_column_text(stmt, 0);
//            if (filename && *filename != 0) {
//                NSString *name = [NSString stringWithUTF8String:filename];
//                if (name) [filenames addObject:name];
//            }
//        } else if (result == SQLITE_DONE) {
//            break;
//        } else {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//            filenames = nil;
//            break;
//        }
//    } while (1);
//    sqlite3_finalize(stmt);
//    return filenames;
//}
//
//- (NSMutableArray *)_dbGetFilepathWithKeys:(NSArray *)keys {
//    if (keys==nil || keys.count==0) return nil;
//    if (![self _dbCheck]) return nil;
//
//    NSString *sql = [NSString stringWithFormat:@"select filepath from disktable where filepath in (%@);", [self _dbJoinedKeys:keys]];
//    sqlite3_stmt *stmt = NULL;
//    int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
//    if (result != SQLITE_OK) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return nil;
//    }
//
//    [self _dbBindJoinedKeys:keys stmt:stmt fromIndex:1];
//    NSMutableArray *filepaths = [NSMutableArray new];
//    do {
//        result = sqlite3_step(stmt);
//        if (result == SQLITE_ROW) {
//            char *filepath = (char *)sqlite3_column_text(stmt, 0);
//            if (filepath && *filepath != 0) {
//                NSString *path = [NSString stringWithUTF8String:filepath];
//                if (path) [filepaths addObject:path];
//            }
//        } else if (result == SQLITE_DONE) {
//            break;
//        } else {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//            filepaths = nil;
//            break;
//        }
//    } while (1);
//    sqlite3_finalize(stmt);
//    return filepaths;
//}
//
//
//
//- (NSMutableArray *)_dbGetFilenamesWithSizeLargerThan:(int)size {
//    NSString *sql = @"select filename from disktable where size > ?1 and filename is not null;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return nil;
//
//    sqlite3_bind_int(stmt, 1, size);
//
//    NSMutableArray *filenames = [NSMutableArray new];
//    do {
//        int result = sqlite3_step(stmt);
//        if (result == SQLITE_ROW) {
//            char *filename = (char *)sqlite3_column_text(stmt, 0);
//            if (filename && *filename != 0) {
//                NSString *name = [NSString stringWithUTF8String:filename];
//                if (name) [filenames addObject:name];
//            }
//        } else if (result == SQLITE_DONE) {
//            break;
//        } else {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//            filenames = nil;
//            break;
//        }
//    } while (1);
//    return filenames;
//}
//
//- (NSMutableArray *)_dbGetFilenamesWithTimeEarlierThan:(int)time {
//    NSString *sql = @"select filename from disktable where last_access_time < ?1 and filename is not null;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return nil;
//    sqlite3_bind_int(stmt, 1, time);
//
//    NSMutableArray *filenames = [NSMutableArray new];
//    do {
//        int result = sqlite3_step(stmt);
//        if (result == SQLITE_ROW) {
//            char *filename = (char *)sqlite3_column_text(stmt, 0);
//            if (filename && *filename != 0) {
//                NSString *name = [NSString stringWithUTF8String:filename];
//                if (name) [filenames addObject:name];
//            }
//        } else if (result == SQLITE_DONE) {
//            break;
//        } else {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//            filenames = nil;
//            break;
//        }
//    } while (1);
//    return filenames;
//}
//
//- (NSMutableArray *)_dbGetItemSizeInfoOrderByTimeAscWithLimit:(int)count {
//    NSString *sql = @"select key, filename, filepath, size from disktable order by last_access_time asc limit ?1;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return nil;
//    sqlite3_bind_int(stmt, 1, count);
//
//    NSMutableArray *items = [NSMutableArray new];
//    do {
//        int result = sqlite3_step(stmt);
//        if (result == SQLITE_ROW) {
//            char *key = (char *)sqlite3_column_text(stmt, 0);
//            char *filename = (char *)sqlite3_column_text(stmt, 1);
//            char *filepath = (char *)sqlite3_column_text(stmt, 2);
//            int size = sqlite3_column_int(stmt, 3);
//            NSString *keyStr = key ? [NSString stringWithUTF8String:key] : nil;
//            if (keyStr) {
//                _XYStoreItem *item = [_XYStoreItem new];
//                item.key = keyStr;
//                item.filename = filename ? [NSString stringWithUTF8String:filename] : nil;
//                item.filepath = filepath ? [NSString stringWithUTF8String:filename] : nil;
//                item.size = size;
//                [items addObject:item];
//            }
//        } else if (result == SQLITE_DONE) {
//            break;
//        } else {
//            if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//            items = nil;
//            break;
//        }
//    } while (1);
//    return items;
//}
//
//- (int)_dbGetItemCountWithKey:(NSString *)key {
//    if (key==nil || key.length==0) return -1;
//    NSString *sql = @"select count(key) from disktable where key = ?1;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return -1;
//    sqlite3_bind_text(stmt, 1, key.UTF8String, -1, NULL);
//    int result = sqlite3_step(stmt);
//    if (result != SQLITE_ROW) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return -1;
//    }
//    return sqlite3_column_int(stmt, 0);
//}
//
//
//- (int)_dbGetTotalItemSize {
//    NSString *sql = @"select sum(size) from disktable;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return -1;
//    int result = sqlite3_step(stmt);
//    if (result != SQLITE_ROW) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return -1;
//    }
//    return sqlite3_column_int(stmt, 0);
//}
//
//- (int)_dbGetTotalItemCount {
//    NSString *sql = @"select count(*) from disktable;";
//    sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
//    if (!stmt) return -1;
//    int result = sqlite3_step(stmt);
//    if (result != SQLITE_ROW) {
//        if (_errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
//        return -1;
//    }
//    return sqlite3_column_int(stmt, 0);
//}
//
//
//#pragma mark - file
//
//- (BOOL)_fileDeleteWithPath:(NSString *)filepath {
//    return [[NSFileManager defaultManager] removeItemAtPath:filepath error:NULL];
//}
//
//+ (BOOL)_fileDeleteWithPath:(NSString *)filepath {
//    return [[NSFileManager defaultManager] removeItemAtPath:filepath error:NULL];
//}
//
//
//- (BOOL)_fileWriteWithName:(NSString *)filename data:(NSData *)data {
//    NSString *path = [_dataPath stringByAppendingPathComponent:filename];
//    return [data writeToFile:path atomically:NO];
//}
//
//- (NSData *)_fileReadWithName:(NSString *)filename {
//    NSString *path = [_dataPath stringByAppendingPathComponent:filename];
//    NSData *data = [NSData dataWithContentsOfFile:path];
//    return data;
//}
//
//- (NSData *)_fileReadWithPath:(NSString *)filepath {
//    NSData *data = [NSData dataWithContentsOfFile:filepath];
//    return data;
//}
//
//
//- (BOOL)_fileDeleteWithName:(NSString *)filename {
//    NSString *path = [_dataPath stringByAppendingPathComponent:filename];
//    return [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
//}
//
//- (BOOL)_fileMoveAllToTrash {
//    CFUUIDRef uuidRef = CFUUIDCreate(NULL);
//    CFStringRef uuid = CFUUIDCreateString(NULL, uuidRef);
//    CFRelease(uuidRef);
//    NSString *tmpPath = [_trashPath stringByAppendingPathComponent:(__bridge NSString *)(uuid)];
//    BOOL suc = [[NSFileManager defaultManager] moveItemAtPath:_dataPath toPath:tmpPath error:nil];
//    if (suc) {
//        suc = [[NSFileManager defaultManager] createDirectoryAtPath:_dataPath withIntermediateDirectories:YES attributes:nil error:NULL];
//    }
//    CFRelease(uuid);
//    return suc;
//}
//
//- (void)_fileEmptyTrashInBackground {
//    NSString *trashPath = _trashPath;
//    dispatch_queue_t queue = _trashQueue;
//    dispatch_async(queue, ^{
//        NSFileManager *manager = [NSFileManager new];
//        NSArray *directoryContents = [manager contentsOfDirectoryAtPath:trashPath error:NULL];
//        for (NSString *path in directoryContents) {
//            NSString *fullPath = [trashPath stringByAppendingPathComponent:path];
//            [manager removeItemAtPath:fullPath error:NULL];
//        }
//    });
//}
//
//
//#pragma mark - private
//
///**
// Delete all files and empty in background.
// Make sure the db is closed.
// */
//- (void)_reset {
//    [[NSFileManager defaultManager] removeItemAtPath:[_path stringByAppendingPathComponent:kDBFileName] error:nil];
//    [[NSFileManager defaultManager] removeItemAtPath:[_path stringByAppendingPathComponent:kDBShmFileName] error:nil];
//    [[NSFileManager defaultManager] removeItemAtPath:[_path stringByAppendingPathComponent:kDBWalFileName] error:nil];
//    [self _fileMoveAllToTrash];
//    [self _fileEmptyTrashInBackground];
//}
//
//#pragma mark - public
//
//- (instancetype)init {
//    @throw [NSException exceptionWithName:@"RWDiskStore init error" reason:@"Please use the designated initializer and pass the 'path'." userInfo:nil];
//    return [self initWithPath:@""];
//}
//
//- (instancetype)initWithPath:(NSString *)path {
//    if (path.length == 0 || path.length > kPathLengthMax) {
//        NSLog(@"RWDiskStore init error: invalid path: [%@].", path);
//        return nil;
//    }
//
//    self = [super init];
//    _path = path.copy;
//    _dataPath = [path stringByAppendingPathComponent:kDataDirectoryName];
//    _trashPath = [path stringByAppendingPathComponent:kTrashDirectoryName];
//    _trashQueue = dispatch_queue_create("com.diskstore.trash", DISPATCH_QUEUE_SERIAL);
//    _dbPath = [path stringByAppendingPathComponent:kDBFileName];
//    _errorLogsEnabled = YES;
//    NSError *error = nil;
//    if (![[NSFileManager defaultManager] createDirectoryAtPath:path
//                                   withIntermediateDirectories:YES
//                                                    attributes:nil
//                                                         error:&error] ||
//        ![[NSFileManager defaultManager] createDirectoryAtPath:[path stringByAppendingPathComponent:kDataDirectoryName]
//                                   withIntermediateDirectories:YES
//                                                    attributes:nil
//                                                         error:&error] ||
//        ![[NSFileManager defaultManager] createDirectoryAtPath:[path stringByAppendingPathComponent:kTrashDirectoryName]
//                                   withIntermediateDirectories:YES
//                                                    attributes:nil
//                                                         error:&error]) {
//        NSLog(@"RWDiskStore init error:%@", error);
//        return nil;
//    }
//
//    if (![self _dbOpen] || ![self _dbInitialize]) {
//        // db file may broken...
//        [self _dbClose];
//        [self _reset]; // rebuild
//        if (![self _dbOpen] || ![self _dbInitialize]) {
//            [self _dbClose];
//            NSLog(@"RWDiskStore init error: fail to open sqlite db.");
//            return nil;
//        }
//    }
//    [self _fileEmptyTrashInBackground]; // empty the trash if failed at last time
//    return self;
//}
//
//- (void)dealloc {
//    UIBackgroundTaskIdentifier taskID = [_XYYSharedApplication() beginBackgroundTaskWithExpirationHandler:^{}];
//    [self _dbClose];
//    if (taskID != UIBackgroundTaskInvalid) {
//        [_XYYSharedApplication() endBackgroundTask:taskID];
//    }
//}
//
//- (BOOL)saveItem:(_XYStoreItem *)item {
//    return [self saveItemWithKey:item.key value:item.value filename:item.filename extendedData:item.extendedData];
//}
//
//- (BOOL)saveItemWithKey:(NSString *)key value:(NSData *)value {
//    NSString *filename = [self _filenameForKey:key];
//    return [self saveItemWithKey:key value:value filename:filename extendedData:nil];
//}
//
//- (BOOL)saveItemWithKey:(NSString *)key value:(NSData *)value filename:(NSString *)filename extendedData:(NSData *)extendedData {
//    if (key.length == 0 || value.length == 0) return NO;
//    if (filename==nil || filename.length== 0) return NO;
//    //写入输入库
//    if (![self _dbSaveWithKey:key filename:filename fileclass:@"NSData" sizebytes:(int)value.length data:value extendedData:extendedData]) {
//        return NO;
//    }
//    //NSData写入文件
//    if (![self _fileWriteWithName:filename data:value]) {
//        return NO;
//    }
//    return YES;
//}
//
//
//
//
//- (BOOL)removeItemForKey:(NSString *)key {
//    if (key==nil || key.length == 0) return NO;
//
//    NSString *filepath = [self _dbGetFilepathWithKey:key];
//    [self _dbDeleteItemWithPath:key];
//    return [self _fileDeleteWithPath:filepath];
//}
//
//- (void)removeItemForKeys:(NSArray *)keys {
//    if (keys.count == 0) return;
//    NSArray *filepaths = [self _dbGetFilepathWithKeys:keys];
//    [self _dbDeleteItemWithKeys:keys];
//    for (NSString *path in filepaths) {
//        [self _fileDeleteWithPath:path];
//    }
//}
//
//- (BOOL)removeAllItems {
//    if (![self _dbClose]) return NO;
//    [self _reset];
//    if (![self _dbOpen]) return NO;
//    if (![self _dbInitialize]) return NO;
//    return YES;
//}
//
//- (void)removeAllItemsWithProgressBlock:(void(^)(int removedCount, int totalCount))progress
//                               endBlock:(void(^)(BOOL error))end {
//
//    int total = [self _dbGetTotalItemCount];
//    if (total <= 0) {
//        if (end) end(total < 0);
//    } else {
//        int left = total;
//        int perCount = 32;
//        NSArray *items = nil;
//        BOOL suc = NO;
//        do {
//            items = [self _dbGetItemSizeInfoOrderByTimeAscWithLimit:perCount];
//            for (_XYStoreItem *item in items) {
//                if (left > 0) {
//                    if (item.filepath) {
//                        [self _fileDeleteWithPath:item.filepath];
//                    }
//                    suc = [self _dbDeleteItemWithKey:item.key];
//                    left--;
//                } else {
//                    break;
//                }
//                if (!suc) break;
//            }
//            if (progress) progress(total - left, total);
//        } while (left > 0 && items.count > 0 && suc);
//        if (suc) [self _dbCheckpoint];
//        if (end) end(!suc);
//    }
//}
//
//- (_XYStoreItem *)getItemForKey:(NSString *)key {
//    if (key==nil || key.length == 0) return nil;
//    _XYStoreItem *item = [self _dbGetItemWithKey:key excludeInlineData:NO];
//    if (item) {
//        [self _dbUpdateAccessTimeWithKey:key];
//        if (item.filename) {
//            item.value = [self _fileReadWithName:item.filename];
//            if (!item.value) {
//                [self _dbDeleteItemWithKey:key];
//                item = nil;
//            }
//        }
//    }
//    return item;
//}
//
//- (_XYStoreItem *)getItemInfoForKey:(NSString *)key {
//    if (key==nil || key.length == 0) return nil;
//    _XYStoreItem *item = [self _dbGetItemWithKey:key excludeInlineData:YES];
//    return item;
//}
//
//- (NSData *)getItemValueForKey:(NSString *)key {
//    if (key==nil || key.length == 0) return nil;
//    NSData *value = nil;
//    NSString *filepath = [self _dbGetFilepathWithKey:key];
//
//    NSString *filepathdb = [self _dbGetFilepathWithKey:key];
//    value = [self _fileReadWithPath:filepathdb];
//    if (!value) {
//        [self _dbDeleteItemWithPath:filepathdb];
//    }else {
//        [self _dbUpdateAccessTimeWithKey:key];
//    }
//    return value;
//}
//
//- (NSArray *)getItemForKeys:(NSArray *)keys {
//    if (keys==nil || keys.count == 0) return nil;
//    NSMutableArray *items = [self _dbGetItemWithKeys:keys excludeInlineData:NO];
//    for (NSInteger i = 0, max = items.count; i < max; i++) {
//        _XYStoreItem *item = items[i];
//        if (item.filepath) {
//            item.value = [self _fileReadWithPath:item.filepath];
//            if (!item.value) {
//                if (item.key) [self _dbDeleteItemWithPath:item.filepath];
//                [items removeObjectAtIndex:i];
//                i--;
//                max--;
//            }
//        }
//    }
//    if (items.count > 0) {
//        [self _dbUpdateAccessTimeWithKeys:keys];
//    }
//    return items.count ? items : nil;
//}
//
//- (NSArray *)getItemInfoForKeys:(NSArray *)keys {
//    if (keys.count == 0) return nil;
//    return [self _dbGetItemWithKeys:keys excludeInlineData:YES];
//}
//
//- (NSDictionary *)getItemValueForKeys:(NSArray *)keys {
//    NSMutableArray *items = (NSMutableArray *)[self getItemForKeys:keys];
//    NSMutableDictionary *kv = [NSMutableDictionary new];
//    for (_XYStoreItem *item in items) {
//        if (item.key && item.value) {
//            [kv setObject:item.value forKey:item.key];
//        }
//    }
//    return kv.count ? kv : nil;
//}
//
//- (BOOL)itemExistsForKey:(NSString *)key {
//    if (key.length == 0) return NO;
//    return [self _dbGetItemCountWithKey:key] > 0;
//}
//
//- (int)getItemsCount {
//    return [self _dbGetTotalItemCount];
//}
//
//- (int)getItemsSize {
//    return [self _dbGetTotalItemSize];
//}
//@end
//
